<?php


namespace UU\Suite\DingTalk\Traits;


use GuzzleHttp\Psr7\Response;
use Psr\Http\Message\ResponseInterface;
use UU\Suite\Kernel\Contracts\EncryptorInterface;
use UU\Suite\Kernel\Message;

trait InteractWithJsonMessage
{
    /**
     * @var EncryptorInterface
     */
    protected $encryptor;

    /**
     * @return ResponseInterface
     * @throws \Throwable
     */
    public function send(): ResponseInterface
    {
        $this->withMessageValidationHandler();

        $messageClass = \sprintf('%s\Message', (new \ReflectionClass($this))->getNamespaceName());

        if (!\class_exists($messageClass)) {
            throw new \RuntimeException($messageClass . ' not found.');
        }

        $message = \call_user_func_array(
            [$messageClass, 'createFromRequest',],
            [
                $this->request,
                $this->encryptor,
            ]
        );

        if ($message->EventType === 'check_url') {
            return $this->createJsonResponse('success', $this->encryptor);;
        }

        if ($message->EventType === 'suite_ticket') {
            $this->getCache()->set('uu.suite.suite_ticket.' . $message->SuiteKey, $message->SuiteTicket);
            return $this->createJsonResponse('success', $this->encryptor);
        }
        $response = $this->handle(new Response(200, [], 'success'), $message);
        if ($response instanceof ResponseInterface) {
            return $response;
        }
        $response = $this->transformResponse($this->normalizeResponse($response), $message);
        $response->getBody()->rewind();
        return $response;
    }

    /**
     * @throws \Throwable
     */
    public function withMessageValidationHandler(): self
    {
        return $this->withHandler(
            function (Message $message, \Closure $next) {
                $query = $this->request->getQueryParams();
                $encrypt = $this->request->getParsedBody()['encrypt'] ?? '';
                $signature = $query['signature'] ?? $query['msg_signature'] ?? null;
//                if (!isset($signature) || 'aes' !== ($query['encrypt_type'] ?? '')) {
//                    return $next($message);
//                }
                $params = [$encrypt, $this->encryptor->getToken(), $query['timestamp'], $query['nonce']];
                sort($params, SORT_STRING);
                if ($signature !== sha1(implode($params))) {
                    throw new \Exception('Invalid request signature.');
                }
                return $next($message);
            }
        );
    }

    /**
     * @param string $type
     * @param callable|string $handler
     * @return $this
     */
    public function addMessageListener(string $type, $handler): self
    {
        $this->withHandler(
            function (Message $message, \Closure $next) use ($type, $handler) {
                return $message->MsgType === $type ? $handler($message) : $next($message);
            }
        );

        return $this;
    }

    /**
     * @param string $event
     * @param callable|string $handler
     * @return $this
     */
    public function addEventListener(string $event, $handler): self
    {
        $this->withHandler(
            function (Message $message, \Closure $next) use ($event, $handler) {
                return $message->Event === $event ? $handler($message) : $next($message);
            }
        );

        return $this;
    }

    public function transformResponse(array $response, Message $message): ResponseInterface
    {
        return $this->createJsonResponse(
            array_filter(
                \array_merge(
                    $message->toArray(),
                    $response
                )
            ),
            $this->encryptor
        );
    }

    /**
     * @param mixed $response
     * @return array
     */
    public function normalizeResponse($response): array
    {
        if (\is_array($response)) {
            if (!isset($response['MsgType'])) {
                throw new \InvalidArgumentException('MsgType cannot be empty.');
            }

            return $response;
        }

        if (is_string($response) || is_numeric($response)) {
            return [
                'MsgType' => 'text',
                'Content' => $response,
            ];
        }

        throw new \InvalidArgumentException(
            sprintf('Invalid Response type "%s".', gettype($response))
        );
    }

    protected function createJsonResponse($attributes, ?EncryptorInterface $encryptor = null): ResponseInterface
    {
        if (is_array($attributes)) {
            $attributes = json_encode($attributes, 256);
        }
        $params = [];
        if ($encryptor) {
            $params = $encryptor->encrypt($attributes);
        }
        return new Response(200, ['Content-Type' => 'application/json'], json_encode($params, JSON_UNESCAPED_UNICODE));
    }
}